package umputun.jutw.Process.from_twi;

import evgeniuz.VKActivity;
import org.jivesoftware.smack.XMPPException;
import umputun.jutw.jabber.JabberClient;
import umputun.jutw.storage.IDataStorage;
import umputun.jutw.twitter.TwitterReadOnly;
import umputun.jutw.utils.logging.ULogger;
import winterwell.jtwitter.Status;
import winterwell.jtwitter.TwitterException;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.List;


/*
 Copyright 2010 Umputun

 This file is part of JUTW.

 JUTW is free software: you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation, either version 3 of the License, or
 (at your option) any later version.

 JUTW is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with JUTW.  If not, see http://www.gnu.org/licenses/.
*/

public class Processor implements Runnable{

	public static Builder newBuilder(TwitterReadOnly twitter, JabberClient jabber) {
		return new Builder(twitter, jabber);
	}

	public void activate() {
		log.info("make process");
		new Thread(this, "Process").start();
		if (pr.no_rt)
			log.info("RT forwarding disabled");
	}


	private Processor(Builder bld) {
		this.pr = bld;
		log.fine(bld.toString());
	}


	@Override
	public void run() {
		log.fine("process activated");

		while(true) {
			try {
				List<Status> my_msgs = null;
				try {
					BigInteger last_st = pr.storage.getUserLastTwiID(pr.twitter.getName());
					if (last_st.longValue() >0)
						pr.twitter.setSinceId(last_st);
					my_msgs = pr.twitter.getUserTimeline();
				}
				catch(TwitterException ex) {
					log.warning("twitter error. message="+ ex.getMessage());
					Thread.sleep(pr.interval_secs * 1000);
					continue;
				}
				boolean first_run_detected = isFirstRun();

				if (first_run_detected) {
					log.info("First-run detected, " + my_msgs.size() + " twitts won't be processed");
				}


				if (my_msgs.size() >0) {

					if (first_run_detected) {
						pr.storage.setUserLastTwiID(pr.twitter.getName(), my_msgs.get(0).getId());
						log.finest("sleeping for " + pr.interval_secs + " seconds");
						Thread.sleep(pr.interval_secs * 1000);
						continue;
					}

					log.fine("got some twits, total="+my_msgs.size());
					List<Status> msgs = new ArrayList<Status>();

					for (int i=my_msgs.size()-1; i>-1; i--) {
						Status status = my_msgs.get(i);
						if (isNewStatus(status) && isEnable(status) && !first_run_detected) {
							if (status.getText().contains("#noj")) {
								log.fine("twit won't be reposted due #noj tag. text="+ status.getText());
								continue;
							}
							if (status.getText().toUpperCase().startsWith("RT @") && pr.no_rt) {
								log.fine("re-twit won't be reposted. text="+ status.getText());
								continue;
							}
							log.info("twit added for reposting. id="+status.getId() + " text=" + status.getText());
							msgs.add(status);
						}
					}

					if (msgs.size() >0) {
						BigInteger last_proc_id = procesMessages(msgs);
						if (last_proc_id.longValue() > 0) {
							pr.storage.setUserLastTwiID(pr.twitter.getName(), last_proc_id);
							log.finest("set last processed id="+ last_proc_id);
						}
					}
				}
				else {
					log.finest("nothing new in twitter");
				}
				log.finest("sleeping for " + pr.interval_secs + " seconds");
				Thread.sleep(pr.interval_secs * 1000);
			}
			catch (InterruptedException e) {
				log.severe(ULogger.getStackTrace(e));
				e.printStackTrace();
				break;
			} catch (IDataStorage.DataStorageException e) {
				log.severe(ULogger.getStackTrace(e));
				e.printStackTrace();
				break;
			}
		}
	}

	/***
	 * process list of twitter statuses. Initiate single jabber connection and send everything
	 * with jdelay
	 * <p>
	 * @param msgs - all new statuses
	 * @return last twitter id for reposted status
	 * @throws InterruptedException - only if was interrupted during sleep
	 * @throws IDataStorage.DataStorageException if failed to access to storage backend
	 */
	private BigInteger procesMessages(List<Status> msgs) throws InterruptedException, IDataStorage.DataStorageException {
		BigInteger result = new BigInteger("-1");
		try {
			pr.jabber.connect();
			for (int i=0; i<msgs.size(); i++) {
				Status status = msgs.get(i);
				String jtext = Translator.translate(status.getText());
				if(pr.expand_links)
					jtext = shchvova.jutw.UrlExpander.Expand(jtext);

				if (pr.jprefix != null) jtext = pr.jprefix + "\n" + jtext;
				if (pr.jsufix != null)  jtext = jtext + "\n" + pr.jsufix;

				log.fine("sending to jabber: " + jtext);
				for(String to_jid : pr.to_jids)				{
					pr.jabber.sendMessage(to_jid, jtext);
				}
				if (null != pr.vkontakte) {
                    try {
                        log.fine("sending to vkontakte: " + jtext);
                        pr.vkontakte.setStatus(jtext);
                    } catch (Exception e) {
                        log.warning("vkontakte error. message="+e.getMessage());
                    }
                }
				result = status.getId();

				if (result.longValue() >0)
					pr.storage.setUserLastTwiID(pr.twitter.getName(), result);

				if (msgs.size() > 1 && i != msgs.size()-1) {
					log.finest("sent. then will sleep for " + pr.jabber_delay + " seconds");
					Thread.sleep(pr.jabber_delay * 1000);
				}
			}
			pr.jabber.disconnect();
			log.finest("jabber disconnected");
		}
		catch (XMPPException e) {
			log.severe(ULogger.getStackTrace(e));
			e.printStackTrace();
		}
		return result;
}

	private boolean isNewStatus(Status status) {
		BigInteger last_id = pr.storage.getUserLastTwiID(pr.twitter.getName());
		if (status.getId().compareTo(last_id) >0) {
			log.fine("new twit. id="+status.getId());
			return true;
		}
		log.fine("old twit ignored. id="+status.getId());
		return false;
	}

	/**
	 * Check if the program running for the first time, in order to avoid massive initial reposting
	 * First time means no user.id file present, i.e. getUserLastID returns 0
	 * The method will return true one time only. Then, regardless getUserLastID it will be false
 	 * @return first-run status
	 */
	private boolean isFirstRun() {
		if (!is_first_run) {
			is_first_run = false;
			return false;
		}
		is_first_run = false;
		return (pr.storage.getUserLastTwiID(pr.twitter.getName()).longValue() == 0);
	}


	private boolean isEnable(Status status) {
		boolean result = !(status.getText().startsWith("@"));
		if (!result) {
			log.fine("twit blocked. text=" + status.getText());
		}
		return result;
	}

	private Builder pr = null;
	private boolean is_first_run = true;
	private static final ULogger log = ULogger.getLogger();


	public static class Builder {

		private Builder(TwitterReadOnly twitter, JabberClient jabber) {
			this.twitter = twitter;
			this.jabber = jabber;
		}

		public Processor build() {
			if (storage == null || twitter == null || jabber == null)
				throw new IllegalStateException("one of mandatory params not good");

			return new Processor(this);
		}

		public Builder vkontakte(VKActivity vkontakte) {
			this.vkontakte = vkontakte;
			return this;
		}

		public Builder interval(int interval_secs) {
			this.interval_secs = interval_secs;
			return this;
		}

		public Builder storage(IDataStorage storage) {
			this.storage = storage;
			return this;
		}

		public Builder to_jids(List<String> to_jids) {
			this.to_jids = to_jids;
			return this;
		}

		public Builder jabber_delay(int jabber_delay) {
			this.jabber_delay = jabber_delay;
			return this;
		}

		public Builder rt_enable(boolean rt_enable) {
			this.no_rt = !rt_enable;
			return this;
		}

		public Builder expand_links(boolean expand_links) {
			this.expand_links = expand_links;
			return this;
		}

		public Builder jprefix(String jprefix) {
			this.jprefix = jprefix;
			return this;
		}

		public Builder jsufix(String jsufix) {
			this.jsufix = jsufix;
			return this;
		}

		@Override
		public String toString() {
			return "Builder [twitter=" + twitter + ", jabber=" + jabber
					+ ", vkontakte=" + vkontakte + ", interval_secs="
					+ interval_secs + ", storage=" + storage + ", to_jids="
					+ to_jids + ", jabber_delay=" + jabber_delay + ", no_rt="
					+ no_rt + ", expand_links=" + expand_links + ", jprefix="
					+ jprefix + ", jsufix=" + jsufix + "]";
		}

		private final TwitterReadOnly twitter;
		private final JabberClient jabber;

		private VKActivity vkontakte = null;
		private int interval_secs = 180;
		private IDataStorage storage = null;
		private List<String> to_jids = new ArrayList<String>();
		private int jabber_delay = 10;
		private boolean no_rt = true;
		private boolean expand_links = false;
		private String jprefix, jsufix;
	}

}
